package com.qinge.blog.config;


import com.qinge.blog.constant.Constant;
import com.qinge.blog.security.*;
import com.qinge.blog.security.filter.JavaWebTokenAuthenticationTokenFilter;
import com.qinge.blog.security.filter.VerifyCodeFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.annotation.Resource;

/**
 * @author QDW
 * @date 2022/4/29 11:24
 * @description WebSecurityConfig
 **/
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    // 验证码过滤器
    @Resource
    VerifyCodeFilter verifyCodeFilter;

    // JWT过滤器
    @Resource
    private JavaWebTokenAuthenticationTokenFilter javaWebTokenAuthenticationTokenFilter;

    // 退出
    @Resource
    LogoutHandlerImpl logoutHandler;

    // 登录成功处理逻辑
    @Resource
    AuthenticationSuccessHandlerImpl authenticationSuccessHandler;

    // 登录失败处理逻辑
    @Resource
    AuthenticationFailureHandlerImpl authenticationFailureHandler;

    // 权限拒绝处理逻辑
    // @Resource
    // CustomizeAccessDeniedHandler accessDeniedHandler;

    // 匿名用户访问无权限资源时的异常
    @Resource
    AuthenticationEntryPointImpl authenticationEntryPoint;

    // 会话失效(账号被挤下线)处理逻辑
    @Resource
    SessionInformationExpiredStrategyImpl sessionInformationExpiredStrategy;

    // 登出成功处理逻辑
    @Resource
    LogoutSuccessHandlerImpl logoutSuccessHandler;

    // 访问决策管理器
    @Resource
    AccessDecisionManagerImpl accessDecisionManager;

    // 实现权限拦截
    @Resource
    FilterInvocationSecurityMetadataSourceImpl securityMetadataSource;

    @Resource
    private SecurityInterceptor securityInterceptor;

    @Override
    @Bean
    public UserDetailsService userDetailsService() {
        // 获取用户账号密码及权限信息
        return new UserDetailsServiceImpl();
    }

    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        // 设置默认的加密方式（强hash方式加密）
        return new BCryptPasswordEncoder();
    }

    /*
     * @author QDW
     * @date 2022/5/20 14:31
     * @description 配置静态资源
     * @param web
     * @return void
     **/
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/upload/file/*");
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // 暂时设置不加密
        auth.userDetailsService(userDetailsService()).
                passwordEncoder(NoOpPasswordEncoder.getInstance());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors().and().csrf().disable();
        // 添加验证码验证
        // http.addFilterBefore(verifyCodeFilter, UsernamePasswordAuthenticationFilter.class);
        // 添加JWT过滤器
        http.addFilterBefore(javaWebTokenAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);

        http.authorizeRequests().
                        // 验证码不需要权限访问
                        // antMatchers("/blog/api/user/captcha**").permitAll().
                        // antMatchers("/common/api/file/upload").permitAll().
                        // antMatchers(Constant.FILE_PREFIX.getValue()).permitAll().
                        withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
                    @Override
                    public <T extends FilterSecurityInterceptor> T postProcess(T t) {
                        t.setAccessDecisionManager(accessDecisionManager);// 决策管理器
                        t.setSecurityMetadataSource(securityMetadataSource);// 安全元数据源
                        return t;
                    }
                }).
                        // 退出
                        and().logout().
                addLogoutHandler(logoutHandler).
                logoutUrl("/blog/api/user/logout").
                logoutSuccessHandler(logoutSuccessHandler).// 登出成功处理逻辑
                deleteCookies("JSESSIONID").// 登出之后删除cookie
                invalidateHttpSession(true). // 无效的Session
                permitAll().// 允许所有用户

                        // 登入
                        and().formLogin().
                loginProcessingUrl("/blog/api/user/login").
                successHandler(authenticationSuccessHandler).// 登录成功处理逻辑
                failureHandler(authenticationFailureHandler).// 登录失败处理逻辑
                permitAll().// 允许所有用户

                        // 异常处理(权限拒绝、登录失效等)
                        and().exceptionHandling().
                        // accessDeniedHandler(accessDeniedHandler).// 权限拒绝处理逻辑
                        authenticationEntryPoint(authenticationEntryPoint).// 匿名用户访问无权限资源时的异常处理

                        // 会话管理
                        and().sessionManagement().
                maximumSessions(1).// 同一账号同时登录最大用户数
                expiredSessionStrategy(sessionInformationExpiredStrategy);// 会话失效(账号被挤下线)处理逻辑
        http.addFilterBefore(securityInterceptor, FilterSecurityInterceptor.class);
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
}
